import gdal
from gdal import ogr
from gdal import osr
from tqdm import tqdm
from time import time
import pypinyin
from pypinyin import pinyin, lazy_pinyin
import os
import cv2
import zipfile
import json
import shutil
import numpy as np
import re
import math


class UniversalTool:

    def __init__(self):
        """
        通用工具
        """
        pass

    @staticmethod
    def ShpToFeatureGeometryDictList(datasourcename, layername=None):
        """
        获得矢量文件所有json格式要素geometry列表
        :param datasourcename: shp文件路径
        :param layername: 图层名称
        :return: 所有要素属性字典列表
        """
        gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8")
        datasource = ogr.Open(datasourcename)
        if layername == 0 or layername is None:
            layer = datasource.GetLayer(0)
        else:
            layer = datasource.GetLayerByName(layername)
        featurecount = layer.GetFeatureCount()
        featuregeometrydictlist = []
        for featureindex in range(featurecount):
            feature = layer.GetFeature(featureindex)
            geometry = feature.geometry()
            geometryjson = geometry.ExportToJson()
            geometrydict = json.loads(geometryjson)
            featuregeometrydictlist.append(geometrydict)
            feature.Destroy()
        datasource.Destroy()
        return featuregeometrydictlist

    @staticmethod
    def ShpToFeatureFieldDictList(datasourcename, layername=None):
        """
        获取shp文件字段字典列表
        :param datasourcename: shp文件路径
        :param layername: 图层名称
        :return: 所有要素字段字典列表
        """
        gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8")
        datasource = ogr.Open(datasourcename)
        if layername == 0 or layername is None:
            layer = datasource.GetLayer(0)
        else:
            layer = datasource.GetLayerByName(layername)
        featurecount = layer.GetFeatureCount()
        featurefielddictlist = []
        for featureindex in range(featurecount):
            feature = layer.GetFeature(featureindex)
            fielddict = feature.items()
            featurefielddictlist.append(fielddict)
            feature.Destroy()
        datasource.Destroy()
        return featurefielddictlist

    @staticmethod
    def ShpToFeatureDictList(datasourcename, layername=None):
        """
        获取shp文件指定图层所有要素的属性与几何的字典列表
        :param datasourcename: shp文件路径
        :param layername: 图层名称
        :return: 要素属性与几何字典的列表
        """
        gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8")
        datasource = ogr.Open(datasourcename)
        if layername == 0 or layername is None:
            layer = datasource.GetLayer(0)
        else:
            layer = datasource.GetLayerByName(layername)
        featurecount = layer.GetFeatureCount()
        featuredictlist = []
        for featureindex in tqdm(range(featurecount)):
            feature = layer.GetFeature(featureindex)
            featurejson = feature.ExportToJson()
            featuredict = json.loads(featurejson)
            featuredictlist.append(featuredict)
            feature.Destroy()
        datasource.Destroy()
        return featuredictlist

    @staticmethod
    def GeometryDictListTransfSrs(geometrydictlist, insrsw, outsrsw):
        """
        转换所有几何字典列表坐标系
        :param geometrydictlist: 需要转换的几何字典列表
        :param insrsw: 几何字典列表中坐标点所在的坐标系wkt字符串格式
        :param outsrsw: 需要转换到的目标坐标系的wkt字符串格式
        :return: 转换后的几何字典列表
        """
        insrs = osr.SpatialReference(wkt=insrsw)
        outsrs = osr.SpatialReference(wkt=outsrsw)
        ct = osr.CreateCoordinateTransformation(insrs, outsrs)
        outgeometrydictlist = []
        for geometrydict in geometrydictlist:
            geometrytype = geometrydict["type"]
            if geometrytype == "Polygon":
                polygon = geometrydict["coordinates"]
                outpolygon = [[
                    list(ct.TransformPoint(point[1], point[0]))[:2]
                    for point in ring]
                    for ring in polygon]
                geometrydict["coordinates"] = outpolygon
                outgeometrydictlist.append(geometrydict)
            elif geometrytype == "MultiPolygon":
                multipolygon = geometrydict["coordinates"]
                outmultipolygon = [[[
                    list(ct.TransformPoint(point[1], point[0]))[:2]
                    for point in ring]
                    for ring in polygon]
                    for polygon in multipolygon]
                geometrydict["coordinates"] = outmultipolygon
                outgeometrydictlist.append(geometrydict)
            else:
                print("非多边形几何体，转换出错！")
                pass
        return outgeometrydictlist

    @staticmethod
    def CreateEmptyShp(datasourcename, layername, srsw, geomtype, fields):
        """
        创建新的空shp文件及图层
        :param fields:
        :param datasourcename: shp文件路径
        :param layername: 图层名称
        :param srsw: wkt字符串格式的坐标系
        :param geomtype: 图层要素几何类型
        :return:
        """
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")
        drivername = "ESRI Shapefile"
        driver = ogr.GetDriverByName(drivername)
        datasource = driver.CreateDataSource(datasourcename)
        srs = osr.SpatialReference(wkt=srsw)
        layer = datasource.CreateLayer(layername, srs, geomtype)
        layer.CreateFields(fields)
        datasource.Destroy()
        return

    @staticmethod
    def CreateEmptyShpFromFieldDictList(shapefilepath, layername, srsw, geomtype, fielddictlist):
        """
        依据字段定义字典列表创建空的shp文件
        :param shapefilepath: shp文件路径
        :param layername: 图层名称
        :param srsw: 参考系
        :param geomtype: 几何类型
        :param fielddictlist: 字段字典列表
        :return:
        """
        gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")
        driver = ogr.GetDriverByName("ESRI Shapefile")
        datasource = driver.CreateDataSource(shapefilepath)
        srs = osr.SpatialReference(wkt=srsw)
        layer = datasource.CreateLayer(layername, srs, geomtype)
        for fielddict in fielddictlist:
            fielddefn = ogr.FieldDefn()
            fielddefn.name = fielddict["name"]
            fielddefn.type = fielddict["type"]
            fielddefn.width = fielddict["width"]
            fielddefn.precision = fielddict["precision"]
            fielddefn.justify = fielddict["justify"]
            layer.CreateField(fielddefn)
            fielddefn.Destroy()
        datasource.Destroy()
        return

    @staticmethod
    def FeatureDictListUpdataShp(featuredictlist, datasourcename, layername=None):
        """
        依据要素字典列表更新shp文件指定图层
        :param featuredictlist: 要素字典列表
        :param datasourcename: shp文件路径
        :param layername: 图层名称
        :return:
        """
        gdal.SetConfigOption("SHAPE_ENCODING", "GBK")
        datasource = ogr.Open(datasourcename, 1)
        if layername == 0 or layername is None:
            layer = datasource.GetLayer(0)
        else:
            layer = datasource.GetLayerByName(layername)
        featuredf = layer.GetLayerDefn()
        for featuredict in featuredictlist:
            propertydict = featuredict["properties"]
            feature = ogr.Feature(featuredf)
            for key in propertydict:
                value = propertydict[key]
                # print(key, value)
                feature.SetField2(key, value)
            geometrydict = featuredict["geometry"]
            geometryjson = json.dumps(geometrydict)
            geometry = ogr.CreateGeometryFromJson(geometryjson)
            feature.SetGeometry(geometry)
            layer.CreateFeature(feature)
            feature.Destroy()
        datasource.Destroy()
        return

    @classmethod
    def MultipleShpFilesMerge(cls, shpfilepathlist, newshppath, newlayername):
        """
        多个shp文件的默认图层，前提是属性一致，合成一个shp文件
        :param shpfilepathlist: 原始shp文件路劲列表
        :param newshppath: 生成的shp文件路径
        :param newlayername: 生成的shp文件图层名称
        :return:
        """
        gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8")
        firstshpfilepath = shpfilepathlist[0]
        datasource = ogr.Open(firstshpfilepath)
        layer = datasource.GetLayer(0)
        srs = layer.GetSpatialRef()
        srsw = srs.ExportToWkt()
        geomtype = layer.GetGeomType()
        fields = layer.schema
        cls.CreateEmptyShp(newshppath, newlayername, srsw, geomtype, fields)
        for shppath in shpfilepathlist:
            featuredictlist = cls.ShpToFeatureDictList(shppath)
            cls.FeatureDictListUpdataShp(featuredictlist, newshppath, layername=newlayername)
        datasource.Destroy()
        return

    @classmethod
    def ShpTransformSrs(cls, dsname, layername, srsw, savedir):
        """
        依据输入坐标字符串修改shp文件目标图层的坐标系
        :param dsname: shp文件夹
        :param layername:目标图层可以为0或者None，但必须指定参数
        :param srsw: 空间坐标系wkt字符串格式
        :param savedir: 新shp文件保存路径，切记不能与原始shp文件同文件夹
        :return:
        """
        srs = osr.SpatialReference(wkt=srsw)
        datasource = ogr.Open(dsname)
        if layername == 0 or layername is None:
            layer = datasource.GetLayer(0)
            layername = layer.GetName()
        else:
            layer = datasource.GetLayerByName(layername)
        layersrs = layer.GetSpatialRef()
        srsname, lysrsname = srs.GetName(), layersrs.GetName()
        if srsname == lysrsname:
            print("栅格与矢量坐标系一致，无需转换！")
            return [dsname, layername]
        layersrsw = layersrs.ExportToWkt()
        geomtype = layer.GetGeomType()
        fields = layer.schema
        outdsname = os.path.join(savedir, os.path.split(dsname)[1])
        cls.CreateEmptyShp(outdsname, layername, srsw, geomtype, fields)
        featuredictlist = cls.ShpToFeatureDictList(dsname, layername)
        geometrydictlist = [featuredict["geometry"] for featuredict in featuredictlist]
        outgeometrydictlist = cls.GeometryDictListTransfSrs(geometrydictlist, layersrsw, srsw)
        outfeaturedictlist = []
        for featuredict, geometrydict in zip(featuredictlist, outgeometrydictlist):
            featuredict["geometry"] = geometrydict
            outfeaturedictlist.append(featuredict)
        cls.FeatureDictListUpdataShp(outfeaturedictlist, outdsname, layername)
        datasource.Destroy()
        return [outdsname, layername]

    @staticmethod
    def ShpLayerIntersectToJson(dsname1, layername1, dsname2, layername2, jsonpath):
        """

        :param dsname1:
        :param layername1:
        :param dsname2:
        :param layername2:
        :param jsonpath:
        :return:
        """
        datasource1 = ogr.Open(dsname1)
        if layername1 == 0 or layername1 is None:
            layer1 = datasource1.GetLayer(0)
        else:
            layer1 = datasource1.GetLayerByName(layername1)
        datasource2 = ogr.Open(dsname2)
        if layername2 == 0 or layername2 is None:
            layer2 = datasource2.GetLayer(0)
        else:
            layer2 = datasource2.GetLayerByName(layernaem2)
        featurecount1 = layer1.GetFeatureCount()
        featurecount2 = layer2.GetFeatureCount()
        fielddictlist = []
        for featureindex1 in range(featurecount1):
            feature1 = layer1.GetFeature(featureindex1)
            geometry1 = feature1.geometry()
            for featureindex2 in range(featurecount2):
                feature2 = layer2.GetFeature(featureindex2)
                geometry2 = feature2.geometry()
                if geometry1.Intersect(geometry2):
                    feature1fielddict = feature1.items()
                    feature2fielddict = feature2.items()
                    fielddict = dict(feature1fielddict, **feature2fielddict)
                    fielddictlist.append(fielddict)
        with open(jsonpath, 'w', encoding="utf-8") as jsonfile:
            json.dump(fielddictlist, jsonfile, ensure_ascii=False, indent=4)
        datasource1.Destroy()
        datasource2.Destroy()
        return

    @staticmethod
    def SelectFieldRangeToFeatureDictList(shapefilepath, layername, fieldname, valuerange):
        """
        依据指定字段的值域筛选要素为字典列表
        :param shapefilepath: shp文件路径
        :param layername: 图层名称
        :param fieldname: 字段名称
        :param valuerange: 值域
        :return:
        """
        gdal.SetConfigOption("SHAPE_ENCODING", "UTF-8")
        [minvalue, maxvalue] = valuerange
        datasource = ogr.Open(shapefilepath)
        if layername == 0 or layername is None:
            layer = datasource.GetLayer(0)
        else:
            layer = datasource.GetLayerByName(layername)
        featurecount = layer.GetFeatureCount()
        featuredictlist = []
        for featureindex in range(featurecount):
            feature = layer.GetFeature(featureindex)
            fieldindex = feature.GetFieldIndex(fieldname)
            fieldvalue = feature.GetField(fieldindex)
            if minvalue is None and maxvalue is not None:
                if fieldvalue <= maxvalue:
                    featurejson = feature.ExportToJson()
                    featuredict = json.loads(featurejson)
                    featuredictlist.append(featuredict)
                # feature.Destroy()
            elif minvalue is not None and maxvalue is None:
                if fieldvalue >= minvalue:
                    featurejson = feature.ExportToJson()
                    featuredict = json.loads(featurejson)
                    featuredictlist.append(featuredict)
                # feature.Destroy()
            elif minvalue is not None and maxvalue is not None:
                if minvalue <= fieldvalue <= maxvalue:
                    featurejson = feature.ExportToJson()
                    featuredict = json.loads(featurejson)
                    featuredictlist.append(featuredict)
                # feature.Destroy()
            else:
                feature.Destroy()
        return featuredictlist

    @staticmethod
    def GetShpFileDefn(shapefilepath, layername=None):
        """
        从一个存在的shp文件中得到创建一个shape文件的必要参数
        :param shapefilepath: shp文件路径
        :param layername: 图层名称
        :return:
        """
        datasource = ogr.Open(shapefilepath)
        if layername == 0 or layername is None:
            layer = datasource.GetLayer(0)
        else:
            layer = datasource.GetLayerByName(layername)
        fieldlist = layer.schema
        fielddictlist = []
        for field in fieldlist:
            fielddict = {
                "name": field.name,
                "type": field.type,
                "width": field.width,
                "justify": field.justify,
                "precision": field.precision}
            fielddictlist.append(fielddict)
        geomtype = layer.GetGeomType()
        srs = layer.GetSpatialRef()
        srsw = srs.ExportToWkt()
        datasource.Destroy()
        return [srsw, geomtype, fielddictlist]

    @classmethod
    def SqlSelectFieldAsShp(cls, dsname, lyname, fieldname, valuerange, dstdsname, dstlyname):
        """
        根据某一字段的值域创建新的shp文件
        :param dsname: 原始shp文件路径
        :param lyname: 原始layer名称
        :param fieldname: 字段名称
        :param valuerange: 值域
        :param dstdsname: 新shp文件路径
        :param dstlyname: 新图层名称
        :return:
        """
        featuredictlist = cls.SelectFieldRangeToFeatureDictList(dsname, lyname, fieldname, valuerange)
        [srsw, geomtype, fielddictlist] = cls.GetShpFileDefn(dsname, lyname)
        cls.CreateEmptyShpFromFieldDictList(dstdsname, dstlyname, srsw, geomtype, fielddictlist)
        cls.FeatureDictListUpdataShp(featuredictlist, dstdsname, dstlyname)
        return

    @staticmethod
    def GetSubFile(pardir):
        """
        获取该文件夹下所有子文件路径
        """
        names = os.listdir(pardir)
        subfiles = []
        for name in names:
            unkownpath = os.path.join(pardir, name)
            if os.path.isfile(unkownpath):
                subfiles.append(unkownpath)
        return subfiles

    @staticmethod
    def GetSubDir(pardir):
        """获得所有子目录完整路径"""
        names = os.listdir(pardir)
        subdirs = []
        for name in names:
            unkownpath = os.path.join(pardir, name)
            if os.path.isdir(unkownpath):
                subdirs.append(unkownpath)
        return subdirs

    @classmethod
    def LayerNamrCnToEn(cls, dsname, lyname):
        """保证图层名一定不含中文字符"""
        ds = ogr.Open(dsname)
        if not lyname:
            ly = ds.GetLayer(0)
        else:
            ly = ds.GetLayerByName(lyname)
        lyname = ly.GetName()
        pylyname = cls.HanziToPinyin(lyname)
        if pylyname != lyname:
            drivername = 'ESRI Shapefile'
            gdal.SetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")
            gdal.SetConfigOption("SHAPE_ENCODING", "GBK")
            driver = ogr.GetDriverByName(drivername)
            shpdir, dssign = os.path.split(dsname)
            rdsname = os.path.join(shpdir, dssign + "_PY")
            if os.path.exists(rdsname):
                shutil.rmtree(rdsname)
            rds = driver.CreateDataSource(rdsname)
            rds.CopyLayer(ly, pylyname)
            rds.Destroy()
            ds.Destroy()
            return [rdsname, pylyname]
        ds.Destroy()
        return [dsname, lyname]

    @classmethod
    def GetNewGeoTansform(cls, geot, point):
        """根据原仿射信息与左上角坐标构建仿射参数"""
        [newgeot0, newgeot3] = cls.PointPCtoSC(geot, point)
        newgeot1, newgeot2 = geot[1], geot[2]
        newgeot4, newgeot5 = geot[4], geot[5]
        newgeot = (newgeot0, newgeot1, newgeot2, newgeot3, newgeot4, newgeot5)
        return newgeot

    @classmethod
    def Getarea(cls, polygon):
        """
        给出任意一个多边形，其顶点坐标依次为（x0，y0），（x1，y1）
        ，（x2，y2），...，（xn，yn）（其中n=2，3，4，…），则其
        面积可表示为：
        """
        n = len(polygon)
        if n < 3:
            return 0
        area = 0
        for i in range(n - 2):
            # 以第一个坐标点为原点，将多边形分割为n-2个三角形，
            # 分别计算每个三角形面积后累加得多边形面积
            area += cls.CalculateTriangleArea(
                polygon[0], polygon[i + 1], polygon[i + 2])
        return abs(area)

    @staticmethod
    def CalculateTriangleArea(pointa, pointb, pointc):
        """向量叉乘法计算三角形面积"""
        trianglearea = 0.5 * (
                (pointb[0] - pointa[0]) * (pointc[1] - pointa[1]) -
                (pointb[1] - pointa[1]) * (pointc[0] - pointa[0]))
        return trianglearea

    @staticmethod
    def RasterToReshapeArr2ds(raspath, shape):
        """依据形状缩放栅格获得波段数组列表"""
        [width, height] = shape
        ras = gdal.Open(raspath)
        raswidth = ras.RasterXSize
        rasheight = ras.RasterYSize
        bandcount = ras.RasterCount
        arr2ds = [
            ras.GetRasterBand.ReadAsArray(0, 0, raswidth, rasheight)
            for index in range(1, bandcount + 1)]
        if rasheight == height and raswidth == width:
            del ras
            return arr2ds
        else:
            outarr2ds = []
            for arr2d in arr2ds:
                reshapearr = cv2.resize(
                    arr2d, (width, height), 0, 0, interpolation=cv2.INTER_NEAREST)
                outarr2ds.append(reshapearr)
            del ras
            return outarr2ds

    @staticmethod
    def RasterReshapeByGeo(raspath, rgeo, rasdir):
        """栅格重采样(依据新仿射信息)"""
        ras = gdal.Open(raspath)
        rasname = os.path.split(raspath)[1]
        rassign = os.path.splitext(rasname)[0]
        geot = ras.GetGeoTransform()
        proj = ras.GetProjection()
        srs = ras.GetSpatialRef()
        width, height, bandc = ras.RasterXSize, ras.RasterYSize, ras.RasterCount
        arr2ds = [
            ras.GetRasterBand(i).ReadAsArray(0, 0, width, height)
            for i in range(1, bandc + 1)]
        datatype = ras.GetRasterBand(1).DataType
        novalue = ras.GetRasterBand(1).GetNoDataValue()
        del ras
        size, fx, fy = (0, 0), geot[1] / rgeo[1], geot[5] / rgeo[5]
        rarr2ds = [
            cv2.resize(arr2d, size, fx=fx, fy=fy, interpolation=cv2.INTER_NEAREST)
            for arr2d in arr2ds]
        rwidth, rheight = int(fx * width), int(fy * height)
        rraspath = os.path.join(rasdir, rassign + r".tif")
        driver = gdal.GetDriverByName("GTiff")
        rras = driver.Create(rraspath, rwidth, rheight, bandc, datatype)
        rras.SetGeoTransform(rgeo)
        rras.SetSpatialRef(srs)
        rras.SetProjection(proj)
        for i in range(1, bandc + 1):
            band = rras.GetRasterBand(i)
            band.WriteArray(rarr2ds[i - 1])
            if novalue is not None:
                band.SetNoDataValue(novalue)
            del band
        del rras
        return rraspath

    @staticmethod
    def RasterReshapeByShp(raspath, shape, rasdir):
        """栅格重采样,未完成"""
        ras = gdal.Open(raspath)
        width, height, bandc = ras.RasterXSize, ras.RasterYSize, ras.RasterCount
        geot = ras.GetGeoTransform()
        proj = ras.GetProjection()
        if height == shape[1] and width == shape[0]:
            del ras
            return raspath
        arr2ds = [
            ras.GetRasterBand(i).ReadAsArray(0, 0, width, height)
            for i in range(1, bandc + 1)]
        rarr2ds = [
            cv2.resize(arr2d, (width, height), 0, 0,
                       interpolation=cv2.INTER_NEAREST)
            for arr2d in arr2ds]

        return

    @staticmethod
    def HanziToPinyin(instr):
        """判断字符串中是否包含中文字符，若有，则用中文拼替换，没有，则返回原字符串"""
        outstrs = list(instr)
        strc = len(outstrs)
        for index in range(strc):
            if '\u4e00' <= outstrs[index] <= '\u9fff':
                hanzistr = outstrs[index]
                pinyinstrs = lazy_pinyin(hanzistr)  # 返回的是拼音列表
                pinyinstr = pinyinstrs[0]
                capitalpp = pinyinstr[0].upper() + pinyinstr[1:]
                outstrs[index] = capitalpp
        outstr = "".join(outstrs)
        return outstr

    @classmethod
    def GetEnglishNameFile(cls, inpath, outdir=None):
        """检查文件名中是否包含中文字符，如果包含，修改文件名并生成相应的文件"""
        basedir, filename = os.path.split(inpath)
        name, extend = os.path.splitext(filename)
        newname = cls.HanziToPinyin(name)
        if newname == name:
            print("%s文件名不包含中文字符！" % filename)
            return inpath
        if outdir is None:
            outpath = os.path.join(basedir, newname + extend)
        else:
            outpath = os.path.join(outdir, newname + extend)
        if os.path.exists(outpath):
            print("目标文件存在退出：\n%s" % outpath)
            return outpath
        shutil.copyfile(inpath, outpath)
        return outpath

    @staticmethod
    def CopyDirToExistDir(sourcefolderpath, savedir):
        """
        复制文件夹已存在的目录中
        :param sourcefolderpath:
        :param savedir:
        :return:
        """
        foldername = os.path.split(sourcefolderpath)[1]
        newfolderpath = os.path.join(savedir, foldername)
        shutil.copytree(sourcefolderpath, newfolderpath)
        return

    @staticmethod
    def CopyFileToExistDir(sourcefilepath, savedir):
        """
        复制文件列表中的文件到指定目录中
        :param sourcefilepath: 需要复制的文件路径列表
        :param savedir: 保存文件的目录
        :return:
        """
        filename = os.path.split(sourcefilepath)[1]
        newfilepath = os.path.join(savedir, filename)
        shutil.copyfile(filepath, newfilepath)
        return

    @staticmethod
    def UnzipFile(zippath, unzipdir):
        """
        解压文件
        :param zippath: 压缩文件路径
        :param unzipdir: 解压问价路径
        :return:
        """
        zipfilesign = os.path.splitext(os.path.split(zippath)[1])[0]
        unzipfilepath = os.path.join(unzipdir, zipfilesign)
        if os.path.exists(unzipfilepath):
            return print(unzipfilepath, ":解压文件存在！")
        else:
            os.makedirs(unzipfilepath)
        rasterzipfile = zipfile.ZipFile(zippath, "r")
        for file in rasterzipfile.namelist():
            rasterzipfile.extract(file, unzipfilepath)
        rasterzipfile.close()
        return unzipfilepath

    @classmethod
    def GetGoalFile(cls, infolder, sign=None):
        """
        搜索获得目标文件路径集
        :param infolder: 搜索的目标文件夹路径
        :param sign: 文件名所包含的字符串
        :return:
        """
        if sign is None:
            return cls.FindAllFile(infolder)
        elif sign[0] == r".":
            filepaths = cls.FindAllFile(infolder)
            rasfilepaths = [
                filepath
                for filepath in filepaths
                if os.path.splitext(os.path.split(filepath)[1])[1] == sign]
            return rasfilepaths
        else:
            filepaths = cls.FindAllFile(infolder)
            rasfilepaths = [
                filepath
                for filepath in filepaths
                if os.path.splitext(os.path.split(filepath)[1])[0] == sign]
            return rasfilepaths

    @classmethod
    def FindAllFile(cls, infolder):
        """寻找该目录下的所有文件"""
        names = os.listdir(infolder)
        filepaths = []
        for name in names:
            unkownfilefolder = os.path.join(infolder, name)
            if os.path.isfile(unkownfilefolder):
                filepaths.append(unkownfilefolder)
            elif os.path.isdir(unkownfilefolder):
                filepaths = filepaths + cls.FindAllFile(unkownfilefolder)
        return filepaths

    @staticmethod
    def DictToJsonFile(jsondir, jsonsign, data):
        """字典转json文件"""
        if not os.path.exists(jsondir):
            return print("保存json文件的目录不存在！")
        jsonpath = os.path.join(jsondir, jsonsign + r".json")
        if os.path.exists(jsonpath):
            return print("json文件存在,无法创建！")
        print("Start Creating：\n%s\n......" % jsonpath)
        with open(jsonpath, 'w', encoding="utf-8") as jsonfile:
            json.dump(data, jsonfile, ensure_ascii=False, indent=4)
        print("End: \n%s" % jsonpath)
        return

    @staticmethod
    def JudgmentYear(year):
        """判断该年是否是瑞年"""
        if (year % 4) == 0:
            if (year % 100) == 0:
                if (year % 400) == 0:  # 整百年能被400整除的是闰年
                    yeardayc = 366
                else:  # 能被4整除的整百年且不能被400整除的是平年
                    yeardayc = 365
            else:  # 非整百年能被4整除的一定是闰年
                yeardayc = 366
        else:  # 不能被4整除的一定是平年
            yeardayc = 365
        return yeardayc

    @classmethod
    def YearMonthDayToNumber(cls, year, month, day):
        """计算某年某月某日是这一年的第几天"""
        monthday = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
        if cls.JudgmentYear(year) == 366:
            monthday[1] = 29
        daycount = sum(monthday[:month - 1]) + day
        return daycount

    @staticmethod
    def LabelmeJpgFile(jpgpath, arr2ds, masks):
        """获取滑窗jpg文件，（先jpg后json）, 对不完整的多边形区域进行0填充处理"""
        img = cv2.merge([arr2ds[2], arr2ds[1], arr2ds[0]])
        polygonarrs = [
            np.array(polygon).astype(int) for polygon in masks]
        cv2.fillPoly(img, polygonarrs, (0, 0, 0))
        cv2.imwrite(jpgpath, img)
        return

    @staticmethod
    def LabelmeJsonFile(jsonpath, jpgpath, labels):
        """获取滑窗json文件"""
        shapes = [
            {
                "label": "dapeng1",
                "line_color": None,
                "fill_color": None,
                "points": label,
                "group_id": None,
                "shape_type": "polygon",
                "flags": {}}
            for label in labels]
        with open(jpgpath, 'rb') as jpgfile:
            imagedata = jpgfile.read()
            newimagedata = base64.b64encode(imagedata).decode('utf-8')
        jpgname = os.path.split(jpgpath)[1]
        img = cv2.imread(jpgpath)
        (height, width, channels) = img.shape
        polygons = {
            "version": "4.2.10",
            "flags": {},
            "shapes": shapes,
            "lineColor": [0, 255, 0, 128],
            "fillColor": [255, 0, 0, 128],
            "imagePath": jpgname,
            "imageData": newimagedata,
            "imageHeight": height,
            "imageWidth": width}
        with open(jsonpath, 'w+') as jsonfile:
            json.dump(polygons, jsonfile, indent=4)
        return

    @staticmethod
    def PointSCtoPC(geot, spacepoint):
        """根据栅格仿射参数计算空间点的像素坐标"""
        (left, xpixelsize, yangle, top, xangle, ypixelsize) = geot
        [xspacedistance, yspacedistance] = spacepoint
        pointx = (xspacedistance - left) / xpixelsize
        pointy = (yspacedistance - top) / ypixelsize
        pixelpoint = [pointx, pointy]
        return pixelpoint

    @staticmethod
    def PointPCtoSC(geot, pixelpoint):
        """根据栅格仿射参数计算像素点的空间坐标"""
        (left, xpixelsize, yangle, top, xangle, ypixelsize) = geot
        [xpixeldistance, ypixeldistance] = pixelpoint
        pointx = left + xpixelsize * xpixeldistance + ypixeldistance * yangle
        pointy = top + ypixeldistance * ypixelsize + xpixeldistance * xangle
        spacepoint = [pointx, pointy]
        return spacepoint

    @staticmethod
    def SplitRaster(raspath, savedir):
        """拆分栅格为多个单个波段栅格"""
        ras = gdal.Open(raspath)
        rassign = os.path.splitext(os.path.split(raspath)[1])[0]
        width, height, bandc = ras.RasterXSize, ras.RasterYSize, ras.RasterCount
        geot = ras.GetGeoTransform()
        proj = ras.GetProjection()
        srs = ras.GetSpatialRef()
        for i in tqdm(range(1, bandc + 1)):
            band = ras.GetRasterBand(i)
            arr = band.ReadAsArray(0, 0, width, height)
            novalue = band.GetNoDataValue()
            datatype = band.DataType
            bandnumstr = "0" * (2 - len(str(i))) + str(i)
            rasbpath = os.path.join(savedir, rassign + "_B%s.tif" % bandnumstr)
            driver = gdal.GetDriverByName("GTiff")
            rasb = driver.Create(rasbpath, width, height, 1, datatype)
            rasb.SetGeoTransform(geot)
            rasb.SetSpatialRef(srs)
            rasb.SetProjection(proj)
            bandb = rasb.GetRasterBand(1)
            if novalue is not None:
                bandb.SetNoDataValue(novalue)
            bandb.FlushCache()
            band.FlushCache()
            del rasb
        del ras
        return

    @classmethod
    def ClipRaster(cls, rasfile, rasdir, winsize):
        """裁剪栅格至指定目录中, 若超出边界，窗口会自适应剩余大小"""
        [xsize, ysize] = winsize
        [shape, datatype, novalue, srsw, geot, proj] = cls.GetParams(rasfile)
        rasname = os.path.split(rasfile)[1]
        sign = os.path.splitext(rasname)[0]
        rasfolder = os.path.join(rasdir, sign + r"_Clip")
        if os.path.exists(rasfolder):
            print("正在更新裁剪结果文件夹！")
        # shutil.rmtree(rasfolder)
        # os.makedirs(rasfolder)
        else:
            os.makedirs(rasfolder)
        [width, height] = shape[:-1]
        xclipc, yclipc = math.ceil(width / xsize), math.ceil(height / ysize)
        clips = [
            [xindex, yindex] for yindex in range(yclipc) for xindex in
            range(xclipc)]
        print("Start Cliping Raster:\n", rasfile)
        print("Generate Folder:\n", rasfolder)
        for index in tqdm(range(len(clips))):
            clipitem = clips[index]
            [xindex, yindex] = clipitem
            xoff, yoff = xindex * xsize, yindex * ysize
            clipid = "0" * (7 - len(str(index))) + str(index)
            name = "%s_%s_%s_%s.tif" % (clipid, sign, str(xindex), str(yindex))
            arrs = cls.ClipRasToArrs(rasfile, xoff, yoff, xsize, ysize)
            newgeot = cls.GetNewGeoTansform(geot, [xoff, yoff])
            file = cls.BuildRaster(
                rasfolder, name, newgeot, srsw, proj, arrs, datatype, novalue)
        print(rasfile, "\n Cliping Raster End!")
        return rasfolder

    @staticmethod
    def ImageOverlay(baseimagepath, floatimagepath, originanchor, saveimagepath):
        """
        图像指定位置叠加
        :param baseimagepath: 底图文件路径
        :param floatimagepath: 顶图文件路径
        :param originanchor: 顶图叠加位置(中心位置参数可以设置为None, 顶图右上角为锚点)
        :param saveimagepath: 文件保存路径
        :return:
        """
        baseimage = cv2.imread(baseimagepath)
        floatimage = cv2.imread(floatimagepath)
        (floatimageheight, floatimagewidth, floatimagechannels) = floatimage.shape
        [x, y] = originanchor
        baseimage[y:y + floatimageheight, x:x + floatimagewidth] = floatimage
        cv2.imwrite(saveimagepath, baseimage)
        return

    def ShpSrsFromRasSrs(self, raspath, dsname, layername, dsdir):
        """
        转换shp文件指定图层的坐标系与输入栅格坐标系一致
        :param raspath: 栅格文件路径
        :param dsname: 矢量文件路径
        :param layername: 图层名称
        :param dsdir:保存矢量结果文件的文件夹
        :return:
        """
        ds = ogr.Open(dsname)
        if layername == 0 or layername is None:
            layer = ds.GetLayer(0)
            layername = layer.GetName()
        else:
            layer = ds.GetLayerByName(layername)
        shpsrs = layer.GetSpatialRef()
        ras = gdal.Open(raspath)
        rassrs = ras.GetSpatialRef()
        rassrsname, shpsrsname = rassrs.GetName(), shpsrs.GetName()
        if rassrsname == shpsrsname:
            print("栅格与矢量坐标系一致，无需转换！")
            return [dsname, layername]
        else:
            [dsname, layername] = self.SPU.ShpToSRSGeomShp(
                dsname, layername, dsdir, rassrsw)
        return [dsname, layername]


UTL = UniversalTool()
